#include "slam/Authenticate/hardware_id.h"

#include <string>

#include "slam/Authenticate/md5.h"

#ifdef __linux__
#include <dirent.h>
#include <string.h>

#include <array>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

std::string exec(const char* cmd) {
  std::array<char, 128> buffer;
  std::string result;
  std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
  if (!pipe) {
    throw std::runtime_error("popen() failed!");
  }
  while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
    result += buffer.data();
  }
  return result;
}

std::string getBiosSerial0(const std::string& c) {
  // dmidecode -t system | grep Serial
  return c + " -t system | grep Serial";
}

/// \brief 获取BIOS版本
std::string getBiosSerial() {
  std::string cmd = getBiosSerial0("dmidecode");
  return exec(cmd.c_str());
}

/// \brief 获取硬盘ID
std::string getDiskId() {
  std::string dev_ret = exec("cat /proc/mounts | grep -E \" / \"");
  auto pos = dev_ret.find(" ");
  if (pos != std::string::npos) {
    std::string device = dev_ret.substr(0, pos);
    std::string cmd =
        "udevadm info --name=" + device + "| grep ID_SERIAL_SHORT";
    std::string id = exec(cmd.c_str());
    return id;
  } else {
    return "123123";
  }
}

/// \brief 遍历文件夹
std::vector<std::string> getListFolders(const std::string& path,
                                        const std::string& exten = "*") {
  std::vector<std::string> list;
  list.clear();

  DIR* dp = nullptr;
  struct dirent* dirp = nullptr;
  if ((dp = opendir(path.c_str())) == nullptr) {
    return list;
  }

  while ((dirp = readdir(dp)) != nullptr) {
    // 寻找类型是link
    if (dirp->d_type == DT_LNK && strcmp(dirp->d_name, ".") != 0 &&
        strcmp(dirp->d_name, "..") != 0) {
      list.emplace_back(static_cast<std::string>(dirp->d_name));
    }
  }

  closedir(dp);
  return list;
}

/// \brief 获取所有mac地址, can总线不包括
std::string getMacAddresses() {
  auto net_devs = getListFolders("/sys/class/net");
  std::string mac_addresses;
  for (auto& it : net_devs) {
    if (it.find("can") == std::string::npos
        && it.find("lo") == std::string::npos
        && it.find("docker") == std::string::npos) {
      std::string file_name = "/sys/class/net/" + it + "/address";
      std::ifstream ifs(file_name);
      std::string mac_string;
      std::getline(ifs, mac_string);
      mac_addresses += mac_string;
      break;
    }
  }
  std::cout << "mac:" << mac_addresses << std::endl;
  return mac_addresses;
}

/// \brief 获取系统id
std::string getSystemId(bool disk_enable, bool bios_enable, bool mac_enable) {
  std::string system_id;
  if (disk_enable) system_id += getDiskId();
  if (bios_enable) system_id += getBiosSerial();
  if (mac_enable) system_id += getMacAddresses();
  return system_id;
}

#endif

#ifdef WIN32
#include <assert.h>

#include <fstream>
#include <iostream>

#include "stdio.h"
#include "tchar.h"
#include "windows.h"

std::string execute(const std::string& command) {
  system((command + " > temp.txt").c_str());

  std::ifstream ifs("temp.txt");
  std::string ret{std::istreambuf_iterator<char>(ifs),
                  std::istreambuf_iterator<char>()};
  ifs.close();  // must close the inout stream so the file can be cleaned up
  if (std::remove("temp.txt") != 0) {
    perror("Error deleting temporary file");
  }
  return ret;
}

/// \brief 获取windows系统的序列号
std::string getSystemId(bool disk_enable, bool bios_enable, bool mac_enable) {
  using namespace std;

  HKEY hkey;
  LPCTSTR data_set = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
  if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, data_set, 0,
                                      KEY_READ | KEY_WOW64_64KEY, &hkey)) {
    char dwValue[256] = {0};
    DWORD dwSzType = REG_SZ;
    DWORD dwSize = sizeof(dwValue);
    LSTATUS ret = RegQueryValueExA(hkey, "ProductId", 0, &dwSzType,
                                   (LPBYTE)&dwValue, &dwSize);
    assert(ret == ERROR_SUCCESS);
    return std::string(dwValue);
  }
  return "12341234";
}

#endif

namespace slam {
namespace authenticate {

/// \brief 硬件id
HardwareId::HardwareId(bool disk_enable, bool bios_enable, bool mac_enable) {
  std::string system_id = getSystemId(disk_enable, bios_enable, mac_enable);
  m_id = MD5(system_id).toStr();
}

/// \brief 硬件id
HardwareId::HardwareId(bool disk_enable, bool bios_enable, bool mac_enable,
                       const std::string& extern_info) {
  std::string system_id =
      getSystemId(disk_enable, bios_enable, mac_enable) + extern_info;
  m_id = MD5(system_id).toStr();
}

std::string HardwareId::GetId() { return m_id; }

}  // namespace authenticate
}  // namespace slam